package jgrouse.jgdoc.ant;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.log.LogChute;

public class VMTask extends Task
{
	private Project m_project;
	private String m_source;
	private String m_resourcePath;
	private List<VMTemplate> m_templates;
	private String m_encoding = "UTF-8";
	private String m_templateEncoding = "UTF-8";
	private int m_chuteLevel = LogChute.WARN_ID;
	
	public VMTask()
	{
		m_templates = new LinkedList<VMTemplate>();
	}
	
	private Object getObj() throws BuildException
	{
		if (m_source == null)
		{
			throw new BuildException("Parameter 'source' must be specified");
		}
		FileInputStream fis = null;
		ObjectInputStream os = null;
		Object obj = null;
		m_project.log("Processing file " + new File(m_source).getAbsolutePath());
		try
		{
			fis = new FileInputStream(m_source);
			os = new ObjectInputStream(fis);
			obj = os.readObject();
		} catch (FileNotFoundException e)
		{
			throw new BuildException("File " + m_source + " not found");
		}			
		catch (Exception e)
		{
			throw new BuildException("Cannot read file " + m_source, e);
		}
		finally
		{
			try
			{
				if (os != null)
				{
					os.close();
				}
				if (fis != null)
				{
					fis.close();
				}
			}
			catch (IOException ex)
			{
				//
			}
		}
		return obj;
	}
	
	@Override
	public void execute() throws BuildException
	{
		Object obj = getObj();
		try
		{
			for (VMTemplate template : m_templates)
			{
				Properties props = new Properties();
        if (getResourcePath() != null) {
          props.put("file.resource.loader.path", getResourcePath());
        }

				if (template.getVmlibs() != null)
				{
					props.put("velocimacro.library", template.getVmlibs());
				}

				props.put(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, Logger.class.getName());

				VelocityEngine ve = new VelocityEngine(props);
				ve.setApplicationAttribute("vmt.task", this);

				Template t = null; 
				t = ve.getTemplate(template.getTemplateName(), getTemplateEncoding());
				for (Output out : template.m_outputs)
				{
					VelocityContext vc = new VelocityContext();
					vc.put(template.m_rootName, obj);
					vc.put("encoding", getEncoding());
					for (Map.Entry<String, String> prop : out.m_options.entrySet())
					{
						vc.put(prop.getKey(), prop.getValue());
					}
					System.out.println("Processing " + template.getTemplateName() + " into " + out.getFile());
					BufferedWriter writer = null;
					FileOutputStream fos = null;
					try
					{
						File f = new File(out.getFile()).getParentFile();
						f.mkdirs();
						fos = new FileOutputStream(out.getFile());
						writer = new BufferedWriter(new OutputStreamWriter(fos, getEncoding()));
						t.merge(vc, writer);
						writer.flush();
					}
					finally
					{
						if (writer != null)
						{
							writer.close();
						}
						if (fos != null)
						{
							fos.close();
						}
					}
				}
			}
		}
		catch (Exception ex)
		{
			throw new BuildException(ex);
		}
	}



	public void addConfiguredTemplate(VMTemplate pTemplate)
	{
		m_templates.add(pTemplate);
	}
	
	
	public static class VMTemplate
	{
		private String m_templateName;
		private String m_vmlibs;
		private List<Output> m_outputs;
		private String m_rootName = "jg";
		
		public VMTemplate()
		{
			m_outputs = new LinkedList<Output>();
		}
		
		public void addConfiguredOutput(Output pOutput)
		{
			m_outputs.add(pOutput);
		}
		
		public String getVmlibs()
		{
			return m_vmlibs;
		}

		public void setVmlibs(String pVmlibs)
		{
			m_vmlibs = pVmlibs;
		}

		public String getTemplateName()
		{
			return m_templateName;
		}

		public void setTemplateName(String pTemplateName)
		{
			m_templateName = pTemplateName;
		}

		public String getRootName()
		{
			return m_rootName;
		}

		public void setRootName(String pRootName)
		{
			m_rootName = pRootName;
		}
	}
	
	public static class Output
	{
		private String m_file;
		private Map<String, String> m_options;
		
		public Output()
		{
			m_options = new HashMap<String, String>();
		}
		
		public void addConfiguredParam(Param param)
		{
			m_options.put(param.getKey(), param.getValue());
		}

		public String getFile()
		{
			return m_file;
		}

		public void setFile(String pFile)
		{
			m_file = pFile;
		}


	}

	public String getResourcePath()
	{
		return m_resourcePath;
	}

	public void setResourcePath(String pResourcePath)
	{
		m_resourcePath = pResourcePath;
	}

	public String getSource()
	{
		return m_source;
	}

	public void setSource(String pSource)
	{
		m_source = pSource;
	}

	public Project getProject()
	{
		return m_project;
	}

	public void setProject(Project pProject)
	{
		m_project = pProject;
	}

	public String getEncoding()
	{
		return m_encoding;
	}

	public void setEncoding(String pEncoding)
	{
		m_encoding = pEncoding;
	}
	
	public static class Logger implements org.apache.velocity.runtime.log.LogChute  
	{
		private VMTask m_task;
		RuntimeServices m_rs;
		

		public void init(RuntimeServices pRs) throws Exception
		{
			m_rs = pRs;
		}
		
		public boolean isLevelEnabled(int pLevel)
		{
			getTask();
			return (m_task != null && pLevel >= m_task.m_chuteLevel);
		}

		public void log(int pLevel, String pMessage)
		{
			log(pLevel, pMessage, null);
		}
		
		private Task getTask()
		{
			if (m_task == null)
			{
				m_task = (VMTask) m_rs.getApplicationAttribute("vmt.task");
			}
			return m_task;
		}

		public void log(int pLevel, String pMessage, Throwable pT)
		{
			getTask();
			if (m_task != null)
			{
				int newLevel = -1;
				switch (pLevel)
				{
					case LogChute.WARN_ID: newLevel = Project.MSG_WARN; break;
					case LogChute.ERROR_ID: newLevel = Project.MSG_ERR; break;
					case LogChute.INFO_ID: newLevel = Project.MSG_INFO; break;
					case LogChute.DEBUG_ID: newLevel = Project.MSG_DEBUG; break;
					default: return;
				}
				this.m_task.log(pMessage, pT, newLevel);
			}
		}
		
	}

	public void setLogLevel(String pLogLevel)
	{
		pLogLevel = pLogLevel.toUpperCase();
		if ("INFO".equals(pLogLevel))
		{
			m_chuteLevel = LogChute.INFO_ID;
		}
		else if ("DEBUG".equals(pLogLevel))
		{
			m_chuteLevel = LogChute.DEBUG_ID;
		}
		else if ("ERROR".equals(pLogLevel))
		{
			m_chuteLevel = LogChute.ERROR_ID;
		}
		else if ("WARN".equals(pLogLevel))
		{
			m_chuteLevel = LogChute.WARN_ID;
		}
	}

    public String getTemplateEncoding()
    {
        return m_templateEncoding;
    }

    public void setTemplateEncoding(String pTemplateEncoding)
    {
        m_templateEncoding = pTemplateEncoding;
    }


}
